package net.nohaven.proj.javeau.crypt.prng;

import net.nohaven.proj.javeau.crypt.engine.Phelix;
import net.nohaven.proj.javeau.utils.Utils;

public class PRNG {
	private static final Object MUTEX = new Object();
	private static final byte[] EMPTY_4K = new byte[4096];
	private static final byte[] USED_4K = new byte[4096];

	private static PRNG instance;

	public static final PRNG getInstance() {
		if (instance == null)
			synchronized (MUTEX) {
				if (instance == null)
					instance = new PRNG();
			}
		return instance;
	}

	private Phelix cipher;
	private byte[] key = new byte[32];
	private byte[] iv = new byte[16];
	private byte[] tick = new byte[16];

	private PRNG() {
		cipher = new Phelix(Phelix.PHELIX_MAC_SIZE);

		Utils.encodeLittleEndian64(System.currentTimeMillis(), key, 0);
		Utils.encodeLittleEndian64(System.nanoTime(), iv, 0);

		cipher.setupKey(Phelix.MODE_ENCRYPT, key);
		cipher.setupNonce(iv);
	}

	public synchronized void getBytes(byte[] b) {
		getBytes(b, 0, b.length);
	}

	public synchronized void getBytes(byte[] b, int off, int len) {
		if (len > EMPTY_4K.length)
			throw new IllegalArgumentException(
					"Cannot ask more than 4k of random data");
		cipher.process(EMPTY_4K, 0, USED_4K, 0, Utils.alignToWord(len, 4));
		System.arraycopy(USED_4K, 0, b, off, len);
	}

	public synchronized void reSeed(byte[] seed) {
		byte[] seed2 = new byte[seed.length];
		cipher.process(seed, 0, seed2, 0, seed.length);
	}

	public synchronized void tick() {
		Utils.encodeLittleEndian64(System.currentTimeMillis(), tick, 0);
		Utils.encodeLittleEndian64(System.nanoTime(), tick, 8);
		reSeed(tick);
	}
}